package filescript;

import exceptions.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;



public class FileAnalyzer {
	
	public static final String FILTER_HEADER = "FILTER";
	public static final String ACTION_HEADER = "ACTION";
	public static final String ORDER_HEADER = "ORDER";
	
	public static final String COMMENT_HEADER = "%";
	
	public static final String GREATER_FILTER = "GREATER";
	public static final String SMALLER_FILTER = "SMALLER";
	public static final String BEFORE_FILTER = "BEFORE";
	public static final String AFTER_FILTER = "AFTER";
	public static final String FILENAME_FILTER = "FILE";
	public static final String READ_FILTER = "READABLE";
	public static final String WRITE_FILTER = "WRITABLE";
	public static final String EXEC_FILTER = "EXECUTABLE";
	
	public static final String YES = "Y";
	public static final String NO = "N";
	public static final String NOT = "NOT";
	
	public static final String PRINT_ACTION = "PRINT";
	public static final String COPY_ACTION = "COPY";
	public static final String MOVE_ACTION = "MOVE";
	public static final String REMOVE_ACTION = "REMOVE";
	public static final String EXEC_ACTION = "EXEC";
	public static final String READ_ACTION = "READ";
	public static final String WRITE_ACTION = "WRITE";

	public static final String ABS_ORDER = "ABS";
	public static final String FILE_ORDER = "FILE";
	public static final String MOD_ORDER = "MOD";
	public static final String SIZE_ORDER = "SIZE";

	public static final int START = -1;
	public static final int FILTERS = 0;
	public static final int ACTIONS = 1;
	public static final int ORDERS = 2;
	public static final int FINALIZE = 3;

	
	private ArrayList<Section> _secs;
	private Section _currentSec;
	private File _file;
	private String _row;
	private String _lastRow;
	private int _status;
	private String _srcDir;

	
	public FileAnalyzer (String src, File file){
		_file = file;		
		_secs = new ArrayList<Section>();
		_currentSec = new Section();
		_status = START;
		_srcDir = src;
		_row = null;
		_lastRow = null;
	}
	
	public ArrayList<Section> analyze() throws IOException, CmdFileException {
		BufferedReader buf = null;
		try {
			buf = new BufferedReader(new FileReader(_file));
			while ((_row = buf.readLine()) != null){
				analyzeRow();
			}
		}
		catch (FileNotFoundException e){
			throw new CmdFilePermissionException();
		}
		finally {		
			buf.close();
		}
		finalizeSection();	
		return _secs;
	}
	
	private void analyzeRow() throws CmdFileException{
		if (_row.startsWith(COMMENT_HEADER)){
			//If the comment is in the first row
			if (_lastRow == null)
				throw new BadOrderException();
			_currentSec.addComment(_row.substring(1));
			return;
		}	
		switch (_status){
			case START: 
				checkSectionStart();
				break;
				 		
			case FILTERS: 
				analyzeFilter();
				break;
			
			case ACTIONS: 
				analyzeAction();
				break;
			
			case ORDERS: 
				analyzeOrder();
				break;
				
			case FINALIZE:
				finalizeSection();
				break;

			default:
		}	
		_lastRow = _row;
	}
	
	private void analyzeFilter() throws CmdFileException {
		String[] rowFilters = _row.split(" ");
		String[] currentFilter;
		String filterType;
		String args;
		boolean hasNot;
		for (String filter : rowFilters){
			hasNot = false;
			currentFilter = filter.split("_");
			filterType = currentFilter[0];
			
			//Only if there is only one command in the row, and one word
			//after splitting with an underscore - i.e header expected.
			if ((rowFilters.length == 1) && (currentFilter.length == 1)){
				if (filterType.equals(FILTER_HEADER) || 
					filterType.equals(ORDER_HEADER))
					throw new BadOrderException();
				
				if (!filterType.equals(ACTION_HEADER))
					throw new BadHeaderException();
				
				if (filterType.equals(ACTION_HEADER)){
					_status = ACTIONS;
					return;
				}
			}			
			
			if (currentFilter.length != 3 && currentFilter.length != 2)
				throw new InvalidFilterTypeException();
			
			if (currentFilter.length == 3){
				if (currentFilter[2].equals(NOT))
					hasNot = true;
				else
					throw new BadParametersException();
			}
			
			args = currentFilter[1];
			
			if (filterType.equals(GREATER_FILTER)){
				createSizeFilter(-1, args , hasNot);
				continue;
			}
			
			if (filterType.equals(SMALLER_FILTER)){
				createSizeFilter(1, args , hasNot);
				continue;
			}
			
			if (filterType.equals(AFTER_FILTER)){
				createDateFilter(-1, args, hasNot);
				continue;
			}
			
			if (filterType.equals(BEFORE_FILTER)){
				createDateFilter(1, args, hasNot);
				continue;
			}
			
			if (filterType.equals(FILENAME_FILTER)){
				_currentSec.addFilter(new filters.NameFilter(args, hasNot));
				continue;
			}
						
			if (filterType.equals(READ_FILTER)){
				createPermissionFilter(1, args, hasNot);
				continue;
			}
			
			if (filterType.equals(WRITE_FILTER)){
				createPermissionFilter(2, args, hasNot);
				continue;
			}
			
			if (filterType.equals(EXEC_FILTER)){
				createPermissionFilter(3, args, hasNot);
				continue;
			}
			
			throw new InvalidFilterTypeException();		
		}
		
		//Marks an end of a row - for the filter manager
		_currentSec.addFilter(null);
		
	}
	
	private void createSizeFilter(int filterNum, String args, boolean hasNot){
		long arg = Long.parseLong(args);
		if (hasNot)
			filterNum *= (-1);
		switch (filterNum){
			//SmallerThan
			case 1: _currentSec.addFilter(new filters.SmallerThan(arg));
					break;
			//GreaterThan
			case -1: _currentSec.addFilter(new filters.LargerThan(arg));
					break;
			default:
		}
	}
	
	private void createDateFilter(int filterNum, String args, boolean hasNot){
		args = args.replaceAll("[/]", "");
		if (hasNot)
			filterNum *= (-1);
		switch (filterNum){
			//BeforeDate
			case 1: _currentSec.addFilter(new filters.BeforeFilter(args));
					break;
			//AfterDate
			case -1: _currentSec.addFilter(new filters.AfterFilter(args));
					break;
			default:
		}
	}
	
	private void createPermissionFilter(int filterNum, String value, 
			boolean hasNot) throws BadParametersException {
		boolean args = checkValidValue(value);
		if (hasNot)
			args = !args;
		switch (filterNum){
			//ReadPermission
			case 1: _currentSec.addFilter(new filters.ReadableFilter(args));
					break;
			//WritePermission
			case 2: _currentSec.addFilter(new filters.WritableFilter(args));
					break;
			//ExecutablePermission
			case 3: _currentSec.addFilter(new filters.ExecutableFilter(args));
					break;
			default:
		}
	}
	
	
	private boolean checkValidValue(String value) 
					throws BadParametersException{
		if (value.equals(YES))
			return true;
		if (value.equals(NO))
			return false;
		throw new BadParametersException();

		
	}
	
	private void analyzeAction() throws CmdFileException {
		
		if (_row.equals(ORDER_HEADER)){
			_status = ORDERS;
			//_hasOrderLine = true;
			return;
		}
		
		if (_row.equals(FILTER_HEADER)){
			//_status = FINALIZE;
			finalizeSection();
			return;
		}
		
		if (hadMoveOrRemove())
			throw new BadOrderException();
		
		if (_row.equals(PRINT_ACTION)){
			_currentSec.addAction(new actions.PrintAction());
			return;
		}
		
		if (_row.equals(REMOVE_ACTION)){
			_currentSec.addAction(new actions.RemoveAction());
			//It can only be the last action!
			//_status = ORDERS;
			return;
		}
						
		String[] action = _row.split("_");
		
		if (action.length != 2)
			throw new InvalidActionTypeException();
		
		String actionType = action[0];
		String args = action[1];
		
		if (actionType.equals(COPY_ACTION)){
			args = analyzePath(args);
			_currentSec.addAction(new actions.CopyAction(_srcDir, args));
			return;
		}
		
		if (actionType.equals(MOVE_ACTION)){
			args = analyzePath(args);
			_currentSec.addAction(new actions.MoveAction(_srcDir, args));
			//It can only be the last action!
			//_status = ORDERS;
			return;
		}
		
		boolean permission = checkValidValue(args);
		
		if (actionType.equals(READ_ACTION)){
			_currentSec.addAction(new actions.SetReadAction(permission));
			return;
		}
		
		if (actionType.equals(WRITE_ACTION)){
			_currentSec.addAction(new actions.SetWriteAction(permission));
			return;
		}
		
		if (actionType.equals(EXEC_ACTION)){
			_currentSec.addAction(new actions.SetExecAction(permission));
			return;
		}
		
		throw new InvalidActionTypeException();		
		
	}
	
	private String analyzePath(String args){
		if (args.startsWith(File.pathSeparator))
			return args.substring(1);
		else
			return args;
		
	}
	
	private void analyzeOrder() throws CmdFileException {
		if (_row.equals(ACTION_HEADER))
			throw new BadOrderException();
		
		if (_row.equals(FILTER_HEADER)){
			//_status = FINALIZE;
			finalizeSection();
			return;
		}
		
		if (_row.equals(ABS_ORDER)){
			_currentSec.setOrder(new orders.AbsComparator());
			_status = FINALIZE;
			return;
		}
		
		if (_row.equals(FILE_ORDER)){
			_currentSec.setOrder(new orders.FileComparator());
			_status = FINALIZE;
			return;
		}
		
		if (_row.equals(MOD_ORDER)){
			_currentSec.setOrder(new orders.ModComparator());
			_status = FINALIZE;
			return;
		}
		
		if (_row.equals(SIZE_ORDER)){
			_currentSec.setOrder(new orders.SizeComparator());
			_status = FINALIZE;
			return;
		}
				
		throw new InvalidOrderTypeException();
		
	}
	
	private boolean hadMoveOrRemove(){
		if (_lastRow.equals(REMOVE_ACTION) ||
			_lastRow.substring(0, 4).equals(MOVE_ACTION))
			return true;
		return false;
	}
	
	private void finalizeSection() throws CmdFileException{
		if (!_currentSec.hasOrder())
			_currentSec.setOrder(new orders.AbsComparator());
		
		if (!_currentSec.hasActions())
			throw new NoActionsFoundException();
		
		_secs.add(_currentSec);
		_currentSec = new Section();
		
		//END OF FILE
		if (_row == null)
			return;
		
		//NEW SECTION EXPECTED
		checkSectionStart();
	}
	
	private void checkSectionStart() throws CmdFileException {
		if ((_row.equals(ACTION_HEADER)) || 
			(_row.equals(ORDER_HEADER)) )
			throw new BadOrderException();
		
		if (!_row.equals(FILTER_HEADER))
			throw new BadHeaderException();
		
		_status = FILTERS;
	}
	
	public static void main (String args[]){
		File file = new File("C:\\Intel\\test2.txt");
		ArrayList<Section> secs = new ArrayList<Section>();
		FileAnalyzer anz = new FileAnalyzer("C:\\Intel\\aaa", file);
		try {
			secs = anz.analyze();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (CmdFileException e) {
			//System.err.println(e);
			System.err.println("Error");
			System.exit(-1);
		}
		for(Section s : secs)
			System.out.println(s);
	}

}
